﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Client; // for SharePoint 2010 
using ServerAPI = Microsoft.SharePoint;
using System.IO;


namespace ImportHub2007Content
{
    partial class Importer
    {

        public Importer(StreamWriter logFile)
        {
            if (logFile == null)
            {
                throw new ArgumentNullException("logFile");
            }

            _logFile = logFile;
        }


        public string TargetUrl
        {
            get { return _targetUrl; }
            set { _targetUrl = value; }
        }

        /// <summary>
        ///  Parse the expected export files and call the related import function
        /// </summary>;
        private void LoadFiles()
        {
            string basePath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Hub2007Exported\\");
            FileStream fs = null;
            System.Xml.Serialization.XmlSerializer xs = null;



            try
            {
                // Import Product data
                Log("Loading products data from file...");
                string productsXml = basePath + "ProductsList\\ProductsListMetadata.xml";
                fs = new FileStream(productsXml, FileMode.Open);
                xs = new System.Xml.Serialization.XmlSerializer(typeof(List<Product>));
                List<Product> importedProducts = (List<Product>)xs.Deserialize(fs);
                fs.Close();

                Log("Products File loaded. Starting products import...");
                ImportProductList(importedProducts);

            }
            catch (Exception err)
            {
                Log("Unexpected error processing Product metadata xml" + err.Message + err.StackTrace);
                return; // abort the rest of the import if the products section import fails
            }

            
            try
            {
                // Import Forums data
                Log("Loading forums data from file...");
                Console.WriteLine("Loading forums data from file...");
                string forumsXml = basePath + "Forums\\ForumsMetadata.xml";
                fs = new FileStream(forumsXml, FileMode.Open);
                xs = new System.Xml.Serialization.XmlSerializer(typeof(List<ForumDiscussionItem>));
                List<ForumDiscussionItem> importedForumItems = (List<ForumDiscussionItem>)xs.Deserialize(fs);
                fs.Close();

                Log("Forums File loaded. Starting forums import...");
                Console.WriteLine("Forums File loaded. Starting forums import...");
                ImportForums(importedForumItems);
            }
            catch (Exception err)
            {
                Log("Unexpected error processing Forums metadata xml" + err.Message + err.StackTrace);
            }
            

            try
            {
                // Import Coach Data
                string coachesXml = basePath + "CoachesList\\CoachesListMetadata.xml";
                fs = new FileStream(coachesXml, FileMode.Open);
                xs = new System.Xml.Serialization.XmlSerializer(typeof(List<Coach>));
                List<Coach> importedCoaches = (List<Coach>)xs.Deserialize(fs);
                fs.Close();

                Log("Coaches File loaded. Starting coaches import...");
                Console.WriteLine("Coaches File loaded. Starting coaches import...");
                ImportCoaches(importedCoaches);

            }
            catch (Exception err)
            {
                Log("Unexpected error processing Coaches metadata xml" + err.Message + err.StackTrace);
            }

            
            try
            {
                // Import Content Lib data
                string contentLibXml = basePath + "ContentLibrary\\ContentLibraryMetadata.xml";
                fs = new FileStream(contentLibXml, FileMode.Open);
                xs = new System.Xml.Serialization.XmlSerializer(typeof(List<ContentLibraryItem>));
                List<ContentLibraryItem> importedContentItems = (List<ContentLibraryItem>)xs.Deserialize(fs);
                fs.Close();

                Log("Content Library File loaded. Starting content library import...");
                ImportContentLibrary(importedContentItems);

            }
            catch (Exception err)
            {
                Log("Unexpected error processing Content Library metadata xml" + err.Message + err.StackTrace);
            }
            
        }


        private void ImportDiscussionReplies(Microsoft.SharePoint.Client.ClientContext ctx, Microsoft.SharePoint.Client.ListItem parentDiscussionItem, ForumDiscussionItem item)
        {
            Microsoft.SharePoint.Client.ListItem newReplyListItem = Microsoft.SharePoint.Client.Utilities.Utility.CreateNewDiscussionReply(ctx, parentDiscussionItem);
            newReplyListItem["Body"] = item.Body;
            newReplyListItem["TrimmedBody"] = item.TrimmedBody;
            newReplyListItem["ThreadIndex"] = item.ThreadIndex;

            newReplyListItem["Author"] = this.GetUserIdFromLogon(item.Author);
            newReplyListItem["Editor"] = this.GetUserIdFromLogon(item.Editor);

            // Before committing a forum item, verify that the product field has a corresponding Product lookup value
            string productItemId = "999";
            string relatedProductTitle = "ImportedProductNotFound";
            string tempProducts = item.Product.Replace(";#", ";").Trim(';');
            string[] forumProducts = tempProducts.Split(';');
            if (forumProducts.Length > 0)
            {
                // grab the first product that is related to this forum post
                relatedProductTitle = forumProducts[0];
            }

            if (IsProductValid(relatedProductTitle, out productItemId))
            {
                // use the product title and product Item ID
                newReplyListItem["Product"] = String.Format("{0};#{1}", productItemId, relatedProductTitle); // this will fail if not in lookup format (1;#Word)
            }
            else
            {
                // could not find a matching product for this forum post, use the default for not found
                Log("ImportForum: Could not associate product to the forum item : " + item.Title);
                newReplyListItem["Product"] = String.Format("{0};#{1}", productItemId, relatedProductTitle); // this will fail if not in lookup format (1;#Word)
            }
            
            Log("ImportForum: Creating reply: " + item.Title);
            newReplyListItem.Update();
            ctx.ExecuteQuery();

            if (item.Replies != null && item.Replies.Count > 0)
            {
                foreach (ForumDiscussionItem reply in item.Replies)
                {
                    ImportDiscussionReplies(ctx, newReplyListItem, reply);
                }
            }

        }


        private void ImportForums(List<ForumDiscussionItem> forumItems)
        {
            Microsoft.SharePoint.Client.ClientContext cc = new Microsoft.SharePoint.Client.ClientContext(this.TargetUrl);
            cc.Credentials = System.Net.CredentialCache.DefaultCredentials;

            Microsoft.SharePoint.Client.Web webHubSource = cc.Web;
            Microsoft.SharePoint.Client.List forumsList = cc.Web.Lists.GetByTitle("Forums");

            foreach (ForumDiscussionItem f in forumItems)
            {
                try
                {
                    Microsoft.SharePoint.Client.ListItem discussionItem = Microsoft.SharePoint.Client.Utilities.Utility.CreateNewDiscussion(cc, forumsList, f.Title);
                    discussionItem["Title"] = f.Title;
                    discussionItem["Body"] = f.Body;
                    discussionItem["ThreadIndex"] = f.ThreadIndex;
                    discussionItem["TrimmedBody"] = f.TrimmedBody;

                    discussionItem["Author"] = this.GetUserIdFromLogon(f.Author);
                    discussionItem["Editor"] = this.GetUserIdFromLogon(f.Editor);

                    // Before committing a forum item, verify that the product field has a corresponding Product lookup value
                    string productItemId = "999";
                    string relatedProductTitle = "ImportedProductNotFound";
                    string tempProducts = f.Product.Replace(";#", ";").Trim(';');
                    string[] forumProducts = tempProducts.Split(';');
                    if (forumProducts.Length > 0)
                    {
                        // grab the first product that is related to this forum post
                        relatedProductTitle = forumProducts[0];
                    }

                    if (IsProductValid(relatedProductTitle, out productItemId))
                    {
                        // use the product title and product Item ID
                        discussionItem["Product"] = String.Format("{0};#{1}", productItemId, relatedProductTitle); // this will fail if not in lookup format (1;#Word)
                    }
                    else
                    {
                        // could not find a matching product for this forum post, use the default for not found
                        Log("ImportForum: Could not associate a product to the forum item : " + f.Title);
                        discussionItem["Product"] = String.Format("{0};#{1}", productItemId, relatedProductTitle); // this will fail if not in lookup format (1;#Word)
                    }

                    discussionItem.Update();
                    Log("ImportForums: Creating thread: " + f.Title);
                    cc.ExecuteQuery();

                    if (f.Replies != null && f.Replies.Count > 0)
                    {
                        foreach (ForumDiscussionItem reply in f.Replies)
                        {
                            ImportDiscussionReplies(cc, discussionItem, reply);
                        }
                    }
                }
                catch (Exception err)
                {

                    Log("Unexpected error occurred processing Forums:");
                    Log(err.Message + err.StackTrace);
                }
            }

            Log("ImportForums: Done importing Forums");
        }

        


        private void ImportProductList(List<Product> products)
        {
            Microsoft.SharePoint.Client.ClientContext cc = new Microsoft.SharePoint.Client.ClientContext(this.TargetUrl);
            cc.Credentials = System.Net.CredentialCache.DefaultCredentials;

            Microsoft.SharePoint.Client.Web webHubSource = cc.Web;
            Microsoft.SharePoint.Client.List list = cc.Web.Lists.GetByTitle("Products");
            Microsoft.SharePoint.Client.CamlQuery cq = new Microsoft.SharePoint.Client.CamlQuery();
            cq.ViewXml = @"<View>
                <Query>
                  <OrderBy><FieldRef Name='Title' Ascending='TRUE'/></OrderBy>
                </Query>
                <RowLimit>900</RowLimit>
              </View>";

            Microsoft.SharePoint.Client.ListItemCollection existingProductItems = list.GetItems(cq);
            cc.Load(existingProductItems,
                 items => items
                     .Include(
                         item => item["ID"],
                         item => item["Title"]));


            cc.ExecuteQuery();

            Microsoft.SharePoint.Client.ListItemCreationInformation itemCreateInfo = new Microsoft.SharePoint.Client.ListItemCreationInformation();
            // go thru the existing product list in SP2010, if the product already exists in the target
            //   remove it from the list of products to import
            Log("ImportProducts: Inspecting Products list in your existing 2010 site collection...");
            foreach (Microsoft.SharePoint.Client.ListItem existingProduct in existingProductItems)
            {
                Product attemptMatch = new Product((string)existingProduct["Title"]);
                if (products.Contains(attemptMatch))
                {
                    // found a match, remove from the list of items to import
                    Log("ImportProducts: Found match for existing product " + attemptMatch.Title + ". To avoid duplicate Product List entries, this product list item won't be added to the Products list.");
                    products.Remove(attemptMatch);
                }
            }

            // for the items remaining to import, create the item in the target list
            foreach (Product p in products)
            {
                try
                {
                    Microsoft.SharePoint.Client.ListItem listItem = list.AddItem(itemCreateInfo);
                    listItem["Title"] = p.Title;
                    listItem["FullTitle"] = p.Title + " imported";
                    listItem["IconUrl"] = "imported";
                    listItem.Update();

                    // Add navigation menu item for this product
                    if (AddProductToNavigation(p.Title))
                    {
                        Log("ImportProducts: Created navigation node for Product " + p.Title);
                    }
                    else
                    {
                        Log("ImportProducts: Could not create navigation node for Product " + p.Title + ". Please configure current navigation manually once import completes.");
                    }
                }
                catch (Exception err)
                {

                    Log("Unexpected error occurred processing Products:");
                    Log(err.Message + err.StackTrace);
                }
            }

            if (products.Count > 0)
            {
                cc.ExecuteQuery();
            }

            Log("ImportProducts: Done importing Products list");
        }


        private void ImportCoaches(List<Coach> coaches)
        {
            Microsoft.SharePoint.Client.ClientContext cc = new Microsoft.SharePoint.Client.ClientContext(this.TargetUrl);
            cc.Credentials = System.Net.CredentialCache.DefaultCredentials;

            Microsoft.SharePoint.Client.Web webHubSource = cc.Web;

            Microsoft.SharePoint.Client.List list = cc.Web.Lists.GetByTitle("Coaches");

            Microsoft.SharePoint.Client.ListItemCreationInformation itemCreateInfo = new Microsoft.SharePoint.Client.ListItemCreationInformation();
            // for the items to import, create the item in the target list
            foreach (Coach c in coaches)
            {
                try
                {
                    Microsoft.SharePoint.Client.ListItem listItem = list.AddItem(itemCreateInfo);
                    listItem["Title"] = c.Title;
                    listItem["PreferredName"] = c.Title;
                    listItem["Availability"] = c.Availability;
                    listItem["ContactMethod"] = c.ContactMethod;

                    // fetch user ID from Ensure User, otherwise don't set  CoachName
                    int retVal = GetUserIdFromLogon(c.DomainUserName);
                    listItem["CoachName"] = retVal + ";#" + c.DomainUserName;

                    listItem["Feature"] = c.Features;
                    listItem["Group"] = c.Group;
                    listItem["Picture"] = c.PictureUrl;

                    // Before committing a coach item, verify that the product field has a corresponding Product lookup value
                    string productItemId = "999";
                    string tempProducts = c.Products.Replace(";#", ";");
                    string[] coachProducts = tempProducts.Split(';');
                    List<string> tempProductLookups = new List<string>();
                    foreach (string s in coachProducts)
                    {
                        if (IsProductValid(s, out productItemId))
                        {
                            tempProductLookups.Add(productItemId + ";#" + s);
                        }
                    }
                    
                    string tempProductLookupValue = "999;#MissingImportedProduct";
                    if (tempProductLookups.Count > 0)
                    {
                        tempProductLookupValue = String.Join(";#", tempProductLookups.ToArray());
                    }
                    // use the joined set of product lookups containing ID and product title 
                    listItem["Product"] = tempProductLookupValue; // this will fail as it is not in lookup format (1;#Word;#2;#Excel)

                    listItem.Update();

                    Log("ImportCoaches: Created listitem for Coach " + c.Title);
                }
                catch (Exception err)
                {

                    Log("Unexpected error occurred processing Coaches:");
                    Log(err.Message + err.StackTrace);
                }
            }
            
            cc.ExecuteQuery();

            Log("ImportCoaches: Done importing Coaches list");
            Console.WriteLine("ImportCoaches: Done importing Coaches list");
        }


        private void ImportContentLibrary(List<ContentLibraryItem> contentItems)
        {
            // use the SERVER API for file upload / property setting
            using (ServerAPI.SPSite siteCollection = new ServerAPI.SPSite(this.TargetUrl))
            {
                using (ServerAPI.SPWeb web = siteCollection.OpenWeb())
                {
                    ServerAPI.SPList contentLibrary = web.Lists["Content Library"];

                    Boolean overwriteAll = false;

                    // for the items to import, create the item in the target list
                    foreach (ContentLibraryItem ci in contentItems)
                    {
                        try
                        {
                            Boolean flagToImportThisFile = false;
                            Boolean fileExist = false;

                            string url = this.TargetUrl + "/ContentLibrary/" + ci.FileName;

                            ServerAPI.SPFile file = web.GetFile(url);
                            if (file != null && file.Exists)
                            {
                                fileExist = true;
                            }

                            if (!overwriteAll)
                            {

                                if (fileExist == true)
                                {
                                    Console.WriteLine("A file named (" + ci.FileName + ") already exists in the target site. Overwrite this file? [Y]es / [N]o / Overwrite [A]ll");
                                    string line = Console.ReadKey().KeyChar.ToString();

                                    if (line.ToLower() == "y") // Check string
                                    {
                                        flagToImportThisFile = true;
                                    }
                                    if (line.ToLower() == "a") // Check string
                                    {
                                        overwriteAll = true;
                                    }
                                }
                                else 
                                {
                                    flagToImportThisFile = true;
                                }
                            }

                            if (overwriteAll || flagToImportThisFile)
                            {
                                string fullFilePath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "Hub2007Exported\\ContentLibrary\\" + ci.FileName);
                                ServerAPI.SPFile uploadedFile = null;

                                using (FileStream fs = new FileStream(fullFilePath, FileMode.Open))
                                {
                                    uploadedFile = contentLibrary.RootFolder.Files.Add(ci.FileName, fs, true);
                                    uploadedFile.Update();
                                }

                                ServerAPI.SPListItem listItem = uploadedFile.Item;
                                listItem["Title"] = ci.Title;
                                listItem["Abstract"] = ci.Abstract;
                                listItem["AppearsInRotator"] = ci.AppearsInRotator;
                                listItem["Difficulty"] = ci.Difficulty;
                                listItem["IsFeaturedContent"] = ci.IsFeaturedContent;
                                listItem["IsVideoContent"] = ci.IsVideoContent;
                                listItem["ThumbnailImageUrl"] = ci.ScreenCaptureImage;

                                string productItemId = "999";
                                string normalizedProductTitle = "ImportedProductNotFound";
                                string tempProducts = ci.Product.Replace(";#", ";").Trim(';');
                                string[] contentProducts = tempProducts.Split(';');
                                if (contentProducts.Length > 0)
                                {
                                    // grab the first product that is related to this forum post
                                    normalizedProductTitle = contentProducts[0];
                                }

                                if (IsProductValid(normalizedProductTitle, out productItemId))
                                {
                                    // use the product title and product Item ID
                                    listItem["Product"] = String.Format("{0};#{1}", productItemId, normalizedProductTitle); // this will fail if not in lookup format (1;#Word)
                                }
                                else
                                {
                                    // could not find a matching product for this forum post, use the default for not found
                                    listItem["Product"] = "999;ImportedProductNotFound";
                                }

                                listItem.Update();
                                Log("ImportContentLibrary: Imported content library item  " + ci.Title);
                            }
                        }

                        catch (IOException e)
                        {
                            Log("IO Exception: The current file could not be imported. ");
                            Log(e.Message);
                        }

                        catch (Exception err)
                        {

                            Log("Unexpected error occurred during file import:");
                            Log(err.Message + err.StackTrace);
                        }
                    }
                }
            }

            Log("ImportContentLibrary: Done importing content library");
        }


        public bool PerformImport()
        {
            if (_logFile == null)
            {
                throw new InvalidOperationException("LogFile was null");
            }

            if (String.IsNullOrEmpty(this.TargetUrl))
            {
                throw new InvalidOperationException("A required parameter is invalid");
            }

            try
            {

                Console.WriteLine("The Import procedure will modify the target site collection and its content. Are you sure you want to continue? [Y]es/[N]o"); // Prompt
                string line = Console.ReadKey().KeyChar.ToString(); // Get string from user
                if (line.ToLower() == "y") // Check string
                {
                    Log("ImportHubContent started with TARGETURL: (" + this.TargetUrl + ")");
                    Log("Attempting URI validation...");

                    bool isTargetValid = IsTargetValid(this.TargetUrl);

                    if (isTargetValid)
                    {
                        LoadFiles();
                    }

                    return true;
                }
                else
                {
                    Log("ImportHubContent was aborted by user.");
                    return false;
                }

            }
            catch (Exception err)
            {
                Log("Unexpected error occurred:");
                Log(err.Message + err.StackTrace);
                return false;

            }
            finally
            {
                if (_logFile != null)
                {
                    _logFile.Flush();
                    _logFile.Close();
                }
            }


        }

    }
}
